﻿//-----------------------------------------------------------------------
// <copyright file="FireRules.cs" company="tuliprules.org">
//     Copyright (c) 2010 tuplelister, tuliprules.org.
// </copyright>
// <summary>This is the FireRules class.</summary>
//-----------------------------------------------------------------------

namespace Tulip.Engine.EngineOperation
{
    using System.Diagnostics;
    using ConflictResolution;
    using Tuples;

    /// <summary>
    /// Represents a 'fire rules' engine operation.   Asserts a collection of facts to the engine.
    /// </summary>
    internal class FireRules : EngineOperation<Agenda>
    {
        /// <summary>
        /// Initializes a new instance of the FireRules class.
        /// </summary>
        /// <param name="session">The current tuple store session.</param>
        /// <param name="agenda">The current agenda.</param>
        public FireRules(TupleStoreSession session, Agenda agenda)
        {
            Debug.Assert(agenda != null, "Agenda cannot be null");

            this.session = session;
            this.data = agenda;
        }

        #region IEngineOperation Members

        /// <summary>
        /// Performs the engine operation.
        /// </summary>
        /// <param name="facts">Collection of facts on which the operation may optionally act.</param>
        /// <returns>Indicates if the engine should start a new match-resolve-do cycle.</returns>
        public override bool Perform(ref object[] facts)
        {
            var agenda = this.data;

            while (agenda.Count > 0)
            {
                var production = agenda.Pop();  // Implements basic refraction
                var productionEngineOps = new ProductionEngineOps();

                // Perform the actions for this production
                productionEngineOps = production.DoActions(productionEngineOps);

                // Processes any modifications performed by actions
                for (var modIdx = 0; modIdx < productionEngineOps.Modifications.Count; modIdx++)
                {
                    if (!productionEngineOps.Retractions.Contains(productionEngineOps.Modifications[modIdx]))
                    {
                        productionEngineOps.Retractions.Add(productionEngineOps.Modifications[modIdx]);
                    }

                    if (!productionEngineOps.Assertions.Contains(productionEngineOps.Modifications[modIdx]))
                    {
                        productionEngineOps.Assertions.Add(productionEngineOps.Modifications[modIdx]);
                    }
                }

                // Process all retractions for the 'fired' production.
                for (var retIdx = 0; retIdx < productionEngineOps.Retractions.Count; retIdx++)
                {
                    this.session.Retract(productionEngineOps.Retractions[retIdx]);
                }

                // Perform all assertions for the 'fired' production
                if (productionEngineOps.Assertions.Count > 0)
                {
                    var newFacts = new object[productionEngineOps.Assertions.Count];
                    productionEngineOps.Assertions.CopyTo(newFacts, 0);

                    // _executor.DoExecute(facts);
                    facts = newFacts;
                    return true;
                }

                facts = new object[0];
            }

            return false;
        }

        #endregion
    }
}
